package com.aps.server.kpi.service;

import com.alibaba.fastjson2.JSON;
import com.aps.algorithm.entity.BasicEquipSchedulingResult;
import com.aps.common.model.genetic.ProcessInterval;
import com.aps.common.service.TestGenericService;
import com.aps.server.entity.ApsOperation;
import com.aps.server.entity.ApsOrder;
import com.aps.server.kpi.GenericDefaultKpiTemplateEngine;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @Author nbZhuozh
 * @Date 2025-11-12  16:16
 *
 *
 * 计算订单相关的指标方法
 *     //TODO 总体公式：订单完成率 = ∑(某工序已完成数量 × 该工序标准工时) / ∑(订单总数量 × 该工序标准工时) × 100% ； 总体计算逻辑就是 ： 订单的已完成工时 / 订单计划的总工时  ps 需要把工序和生产数量和消耗时间  换算成工时
 *     //换算 ： (工序a 已完成工时  +  工序b 已完成工时) / 订单生产数量 * （工序a标准工时 +  工序b标准工时 .....）
 *     //工序a已经完成的数量 * 工序a标准工时 = 工序a 已消耗工时 ；
 *     //工序a 生产数量 * 工序 a标准工时 = 工序a总消耗工时；
 *     //换算 ： ∑(f(工序i已消耗工时)) /  ∑(f(工序i总消耗工时))
 *
 *     //工序已完成数量： 基于某一个时间点，结合 工序排产明细 找到 对应点前完成的数量
 *     //标准工时：工序的 结束时间 - 开始时间 / 工序完成产品数量
 **/

@Service
@Slf4j
public class OrderKpiCalculatedService extends TestGenericService {

    //平均订单交付率
    public BigDecimal calAvgOrderDeliveryRate(GenericDefaultKpiTemplateEngine.KpiBizReference reference){
        List<BasicEquipSchedulingResult> basicEquipSchedulingResultList = reference.getBasicEquipSchedulingResultList();
        Map<String, List<BasicEquipSchedulingResult>> collect = basicEquipSchedulingResultList.stream()
                .collect(Collectors.groupingBy(BasicEquipSchedulingResult::getOrderId));
        //按订单分组
        BigDecimal total = new BigDecimal(0);
        BigDecimal use = new BigDecimal(0);
        for (Map.Entry<String, List<BasicEquipSchedulingResult>> enrty : collect.entrySet()) {
            //获取业务 计划完成日期
            ApsOrder apsOrder = reference.getBasicOrderMap().get(enrty.getKey());
            LocalDateTime finishTime = apsOrder.getDeliveryDate().atStartOfDay();
            total = total.add(calOrderWorkingHours(reference,enrty.getValue()));
            use = use.add(calOrderEndDateWorkingHours(reference,enrty.getValue(),finishTime));
        }
        //计算
        if(total.compareTo(BigDecimal.ZERO) <=0){
            return BigDecimal.ZERO;
        }
        return use.divide(total, 2, RoundingMode.HALF_UP);
    }

    //计算各个订单的完成率
    public Map<String,BigDecimal> calOrderDeliveryRate(GenericDefaultKpiTemplateEngine.KpiBizReference reference){
        Map<String , BigDecimal> resultmap = Map.of();
        List<BasicEquipSchedulingResult> basicEquipSchedulingResultList = reference.getBasicEquipSchedulingResultList();
        Map<String, List<BasicEquipSchedulingResult>> collect = basicEquipSchedulingResultList.stream()
                .collect(Collectors.groupingBy(BasicEquipSchedulingResult::getOrderId));
        //按订单分组
        for (Map.Entry<String, List<BasicEquipSchedulingResult>> enrty : collect.entrySet()) {
            //获取业务 计划完成日期
            ApsOrder apsOrder = reference.getBasicOrderMap().get(enrty.getKey());
            LocalDateTime finishTime = apsOrder.getDeliveryDate().atStartOfDay();
            BigDecimal bigDecimal = calOrderDeliveryRate(reference, enrty.getValue(), finishTime);
            resultmap.put(enrty.getKey(), bigDecimal);
        }
        return resultmap;
    }


    //计算某个订单完成比例 （工时维度）
    public BigDecimal calOrderDeliveryRate(GenericDefaultKpiTemplateEngine.KpiBizReference reference ,
                                           List<BasicEquipSchedulingResult> orderResultList ,
                                           LocalDateTime finishTime){
        if(CollectionUtils.isEmpty(orderResultList)){
            return BigDecimal.ZERO;
        }
        BigDecimal res = new BigDecimal(0);
        BigDecimal total = calOrderWorkingHours(reference, orderResultList);
        if(total != null && total.compareTo(BigDecimal.ZERO) > 0){
            BigDecimal use = calOrderEndDateWorkingHours(reference,orderResultList,finishTime);
            res = use.divide(total, 2, BigDecimal.ROUND_HALF_UP);
        }
        return res;
    }




    /**
     * 根据订单的相关工序明细。计算某一个订单的 理论总消耗工时
     * x = ∑(工序i生产数量 * 工序i标准工时)
     * @param orderResultList
     * @return
     */
    public BigDecimal calOrderWorkingHours(GenericDefaultKpiTemplateEngine.KpiBizReference reference ,
                                           List<BasicEquipSchedulingResult> orderResultList){
        if(CollectionUtils.isEmpty(orderResultList)){
            return BigDecimal.ZERO;
        }
        BigDecimal res = new BigDecimal(0);
        for (BasicEquipSchedulingResult basicEquipSchedulingResult : orderResultList) {
            //获取对应工序
            ApsOperation apsOperation = reference.getBasicOperationMap().get(basicEquipSchedulingResult.getProcessId());
            //获取工序标准时长
            BigDecimal operationTotalStdTime = apsOperation.getOperationTotalStdTime();
            //获取工序完成数量
            BigDecimal processCount = new BigDecimal(basicEquipSchedulingResult.getProcessCount());
            //工序i理论总耗时 = 工序i生产数量 * 工序i标准工时
            res = res.add(processCount.multiply(operationTotalStdTime));
        }
        log.info("【KPI GENERICE TEMPLATE CAL】订单：{}，工序总耗时：{} (分钟)" , orderResultList.get(0).getOrderId(), res);
        return res;
    }

    //截至到交付日期，订单的总工时
    public BigDecimal calOrderEndDateWorkingHours(GenericDefaultKpiTemplateEngine.KpiBizReference reference ,
                                                  List<BasicEquipSchedulingResult> orderResultList ,
                                                  LocalDateTime orderEndDateTime){
        if(CollectionUtils.isEmpty(orderResultList)){
            return BigDecimal.ZERO;
        }
        BigDecimal res = new BigDecimal(0);
        for (BasicEquipSchedulingResult basicEquipSchedulingResult : orderResultList) {
            res = res.add(calProcessWorkingHours(reference,basicEquipSchedulingResult,orderEndDateTime));
        }
        log.info("【KPI GENERICE TEMPLATE CAL】订单：{}，工序总耗时：{} (分钟)" , orderResultList.get(0).getOrderId(), res);
        return res;
    }


    /**
     * 获取某一个工序的总消耗工时
     */
    public BigDecimal calProcessWorkingHours(GenericDefaultKpiTemplateEngine.KpiBizReference reference ,
                                             BasicEquipSchedulingResult equipSchedulingResult,
                                             LocalDateTime orderEndDateTime){
        if (ObjectUtils.isEmpty(equipSchedulingResult)){
            return BigDecimal.ZERO;
        }
        BigDecimal res = new BigDecimal(0);
        //获取对应工序
        ApsOperation apsOperation = reference.getBasicOperationMap().get(equipSchedulingResult.getProcessId());
        //获取工序标准时长
        BigDecimal operationTotalStdTime = apsOperation.getOperationTotalStdTime();
        //获取工序完成数量
        BigDecimal processCount = new BigDecimal(equipSchedulingResult.getProcessCount());
        //当工序开始日期在 交付日期 之后 ， 说明在交期的时候这个工序还没开始干活，所以为 0；
        if(equipSchedulingResult.getProcessStartTime().compareTo(orderEndDateTime) >= 0){
            return res;
        }
        //当工序结束日期 在 交付日期前 ， 直接换算工时
        if(equipSchedulingResult.getProcessEndTime().compareTo(orderEndDateTime) <= 0){
            res = res.add(processCount.multiply(operationTotalStdTime));
            return res;
        }
        //超期工序处理：
        String processIntervalJsonStr = equipSchedulingResult.getProcessIntervalJsonStr();
        if(!StringUtils.isEmpty(processIntervalJsonStr)){
            List<ProcessInterval> processIntervals = parseProcessIntervalsDetails(processIntervalJsonStr);
            //计算 交付日期内已经 完成的工时
            List<ProcessInterval> collect = processIntervals.stream().filter(i -> {
                return i.getEndTime() != null && orderEndDateTime.compareTo(i.getEndTime()) >= 0;
            }).toList();
            //计算 该工序处理的数量
            int size = collect.size();
            //计算 已完成的 对应工时
            res = res.add(new BigDecimal(size).multiply(operationTotalStdTime));
        }
        return res;
    }


    public static List<ProcessInterval> parseProcessIntervalsDetails(String jsonStr){
        if(StringUtils.isEmpty(jsonStr)){
            return Collections.emptyList();
        }
        return JSON.parseArray(jsonStr, ProcessInterval.class);
    }
}
